package zgh.singleton;

import java.io.*;
import java.lang.reflect.Constructor;

/**
 * @author zhanggonghui
 * @create 2022-04-16 22:40
 * @describe 破坏单例模式
 **/
public class DestructionSingletonMode {

    /**
     * 问：单例模式绝对安全么？ 答：不一定的，其实单例也是有可能被破坏的。单例模式其实是对外隐藏了构造函数，保证用户无法主动创建对象。但是实际上我们是有办法可以破坏他的。
     */

    // 构建一个双重检查锁的单例，用于破坏测试
    private volatile static DestructionSingletonMode singleton1;

    private DestructionSingletonMode() {
    }

    public static DestructionSingletonMode getSingleton1() {
        if (singleton1 == null) {
            synchronized (DestructionSingletonMode.class) {
                if (singleton1 == null) {
                    singleton1 = new DestructionSingletonMode();
                }
            }
        }
        return singleton1;
    }

    /**
     * 1.反射破坏单例
     * 使用双重校验锁方式实现单例
     * 这个单例模式提供了一个private类型的构造函数，正常情况下，我们无法直接调用对象的私有方法。但是反射技术给我们提供了一个后门。
     * <p>
     * 怎么防止通过反射，创建单例bean。说白了。通过反射，获取到了私有构造器并取消权限检查，导致了破坏单例bean
     * 可在私有构造方法中假如，if (singleton != null) { throw new RuntimeException("Singleton constructor is called... "); }
     */
    static class SingletonTest {
        public static void main(String[] args) {
            // 获取单例1
            DestructionSingletonMode singleton = DestructionSingletonMode.getSingleton1();
            try {
                // 根据class的全限定名，获取Class对象
                Class<DestructionSingletonMode> singleClass = (Class<DestructionSingletonMode>) Class.forName("com.zgh.singleton.DestructionSingletonMode");
                // 返回所有构造器
                Constructor<DestructionSingletonMode> constructor = singleClass.getDeclaredConstructor(null);
                // 取消对权限的检查,实现对私有的访问和赋值
                constructor.setAccessible(true);
                // 获取单例2
                DestructionSingletonMode singletonByReflect = constructor.newInstance();
                // 打印正常途径获取的单例1
                System.out.println("singleton : " + singleton);
                // 打印反射获取的单例2
                System.out.println("singletonByReflect : " + singletonByReflect);
                // 对比内存地址，返回 false
                System.out.println("singleton == singletonByReflect : " + (singleton == singletonByReflect));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 2.序列化破坏单例
     * 怎么防止这种问题：在单例类中增加代码(详见对应类注释)
     */
    static class SingletonTest1 {
        public static void main(String[] args) {
            // 获取单例1
            Singleton singleton = Singleton.getSingleton();

            // 输出流
            ObjectOutputStream oos = null;
            try {
                oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
                // 写入流
                oos.writeObject(singleton);
                // 再从流中读取
                File file = new File("tempFile");
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
                // 获取到单例2
                Singleton singletonBySerialize = (Singleton) ois.readObject();
                //判断是否是同一个对象，通过先序列化再反序列化的方式，可获取到一个新的单例对象，这就破坏了单例
                // 因为在对象反序列化的过程中，序列化会通过反射调用无参数的构造方法创建一个新的对象
                System.out.println("singleton : " + singleton);
                System.out.println("singletonBySerialize : " + singletonBySerialize);
                System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
